package de.dushuhu.dyndns;

import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.Timer;
import java.util.TimerTask;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.name.Named;

public class CLI {

	private static final Logger logger = LoggerFactory.getLogger(CLI.class);

	private static final Pattern pattern = Pattern.compile("\\b"
			+ "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])" + "\\."
			+ "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])" + "\\."
			+ "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])" + "\\."
			+ "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])" + "\\b");

	private CommandLineParser parser;

	private Options options;

	private HelpFormatter formatter;

	private String help;

	private UpdateService svc;

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		Injector injector = Guice.createInjector(new AgentModule());
		CLI cli = injector.getInstance(CLI.class);
		cli.exec(args);

		return;
	}

	@Inject
	public CLI(CommandLineParser parser, Options options,
			HelpFormatter formatter, @Named("help") String help,
			UpdateService svc) {
		this.parser = parser;
		this.options = options;
		this.formatter = formatter;
		this.help = help;
		this.svc = svc;
	}

	public void exec(String[] args) {

		try {
			final CommandLine cmd = parser.parse(options, args, true);
			final String hostname = cmd.getOptionValue("hostname");
			final String username = cmd.getOptionValue("username");
			final String password = cmd.getOptionValue("password");

			final Timer timer = new Timer("update dyndns");
			TimerTask task = new TimerTask() {

				@Override
				public void run() {

					try {
						String hostAddress = parseHostAddress(cmd);
						svc.update(hostAddress, hostname, username, password);
					} catch (ParseException e) {
						timer.cancel();
						System.out.println(e.getMessage());
						formatter.printHelp(help, options);
					} catch (Exception e) {
						System.out.println(e.getMessage());
						logger.error("failure", e);
					}

					return;
				}
			};
			timer.schedule(task, 0, 60000);

		} catch (ParseException e) {
			System.out.println(e.getMessage());
			formatter.printHelp(help, options);
		}

		return;
	}

	private String parseHostAddress(CommandLine cmd) throws ParseException,
			IOException {

		String hostAddress;

		String[] args = cmd.getArgs();
		switch (args.length) {
		case 0:
			// hostAddress = InetAddress.getLocalHost().getHostAddress();
			// throw new ParseException("missing ip regex");
			hostAddress = selectPublic();
			break;
		case 1:
			hostAddress = selectLocal(args[0]);
			break;
		default:
			throw new ParseException("only one ip regex is allowed");
		}

		return hostAddress;
	}

	/**
	 * http://www.dyndns.com/developers/checkip.html
	 * 
	 * @return
	 * @throws IOException
	 * @throws ClientProtocolException
	 */
	private String selectPublic() throws IOException {

		String hostAddress;

		HttpClient httpclient = new DefaultHttpClient();
		try {
			HttpGet httpget = new HttpGet("http://checkip.dyndns.org/");

			System.out.println("executing request " + httpget.getURI());

			// Create a response handler
			ResponseHandler<String> responseHandler = new BasicResponseHandler();
			String responseBody = httpclient.execute(httpget, responseHandler);
			System.out.println("----------------------------------------");
			System.out.println(responseBody);
			System.out.println("----------------------------------------");

			/*-
			 * <html><head><title>Current IP Check</title></head><body>Current IP Address: 117.80.82.13</body></html>			
			 */
			Matcher m = pattern.matcher(responseBody);
			if (m.find()) {
				hostAddress = m.group();
			} else {
				throw new UnknownHostException(responseBody);
			}

		} finally {
			// When HttpClient instance is no longer needed,
			// shut down the connection manager to ensure
			// immediate deallocation of all system resources
			httpclient.getConnectionManager().shutdown();
		}

		return hostAddress;
	}

	private String selectLocal(String regex) throws SocketException,
			UnknownHostException {

		String hostAddress;

		Pattern p = Pattern.compile(regex);

		hostAddress = null;
		Enumeration<NetworkInterface> interfaces = NetworkInterface
				.getNetworkInterfaces();
		while (interfaces.hasMoreElements()) {
			NetworkInterface networkInterface = interfaces.nextElement();
			// System.out.println(i.getName());
			Enumeration<InetAddress> addresses = networkInterface
					.getInetAddresses();
			while (addresses.hasMoreElements()) {
				InetAddress inetAddress = addresses.nextElement();
				if (!inetAddress.isLoopbackAddress()
						&& !inetAddress.isLinkLocalAddress()
						&& inetAddress instanceof Inet4Address) {
					// System.out.println(a.isAnyLocalAddress());
					String ip = inetAddress.getHostAddress();
					// System.out.println(ip);
					if (p.matcher(ip).matches()) {
						hostAddress = ip;
					}
				}
			}
		}
		if (hostAddress == null) {
			/*
			 * wait and check again
			 */
			throw new UnknownHostException("no ip matches regex: " + regex);
		}

		return hostAddress;
	}
}
